﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using Creventive.Wpf.ShellFactory.Utility;

namespace Creventive.Wpf.ShellFactory.Workspace.Primitives
{
    /// <summary>
    ///   Presents the provided application view.
    /// </summary>
    public class ApplicationViewPresenter : FrameworkElement
    {
        #region Static Fields/Constants

        /// <summary>
        /// Backing field for the ApplicationView dependency property
        /// </summary>
        public static readonly DependencyProperty ApplicationViewProperty = DependencyProperty.Register("ApplicationView", typeof(ApplicationView), typeof(ApplicationViewPresenter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, (s, e) => ((ApplicationViewPresenter)s).OnApplicationViewChanged((ApplicationView)e.OldValue, (ApplicationView)e.NewValue)));

        #endregion

        private BindingExpressionBase autoBindingExpression;

        /// <summary>
        ///   Gets or sets the workspace item to display.
        /// </summary>
        /// <value>The workspace item.</value>
        public ApplicationView ApplicationView
        {
            get { return (ApplicationView)this.GetValue(ApplicationViewProperty); }
            set { this.SetValue(ApplicationViewProperty, value); }
        }

        /// <summary>
        ///   Gets the number of visual child elements within this element.
        /// </summary>
        /// <value></value>
        /// <returns>The number of visual child elements for this element.</returns>
        protected override int VisualChildrenCount
        {
            get
            {
                var item = this.ApplicationView;
                return (item != null) ? 1 : 0;
            }
        }

        /// <summary>
        ///   Called when the ApplicationView property has changed.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        protected virtual void OnApplicationViewChanged(ApplicationView oldValue, ApplicationView newValue)
        {
            if (oldValue == newValue)
                return;

            if (oldValue != null)
                this.RemoveVisualChild(oldValue);

            if (!DesignerProperties.GetIsInDesignMode(this) && newValue != null)
                this.AddVisualChild(newValue);
        }

        /// <summary>
        ///   Overrides <see cref = "M:System.Windows.Media.Visual.GetVisualChild(System.Int32)" />, and returns a child at the specified index from a collection of child elements.
        /// </summary>
        /// <param name = "index">The zero-based index of the requested child element in the collection.</param>
        /// <returns>
        ///   The requested child element. This should not return null; if the provided index is out of range, an exception is thrown.
        /// </returns>
        protected override Visual GetVisualChild(int index)
        {
            var item = this.ApplicationView;
            if (item == null)
                throw new ArgumentOutOfRangeException("index");
            else
                return item;
        }

        /// <summary>
        ///   When overridden in a derived class, measures the size in layout required for child elements and determines a size for the <see cref = "T:System.Windows.FrameworkElement" />-derived class.
        /// </summary>
        /// <param name = "availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>
        ///   The size that this element determines it needs during layout, based on its calculations of child element sizes.
        /// </returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            var item = this.ApplicationView;
            if (item != null)
            {
                item.Measure(availableSize);
                return item.DesiredSize;
            }
            else
                return base.MeasureOverride(availableSize);
        }

        /// <summary>
        ///   When overridden in a derived class, positions child elements and determines a size for a <see cref = "T:System.Windows.FrameworkElement" /> derived class.
        /// </summary>
        /// <param name = "finalSize">The final area within the parent that this element should use to arrange itself and its children.</param>
        /// <returns>The actual size used.</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            var item = this.ApplicationView;
            if (item != null)
            {
                item.Arrange(new Rect(finalSize));
                return item.RenderSize;
            }
            else
                return base.ArrangeOverride(finalSize);
        }

        /// <summary>
        ///   Invoked when the parent of this element in the visual tree is changed. Overrides <see cref = "M:System.Windows.UIElement.OnVisualParentChanged(System.Windows.DependencyObject)" />.
        /// </summary>
        /// <param name = "oldParent">The old parent element. May be null to indicate that the element did not have a visual parent previously.</param>
        protected override void OnVisualParentChanged(DependencyObject oldParent)
        {
            if ((this.ReadLocalValue(ApplicationViewProperty) == DependencyProperty.UnsetValue) || this.GetBindingExpression(ApplicationViewProperty) == this.autoBindingExpression)
            {
                WorkspaceItem workspaceItem = this.FindVisualAncestor<WorkspaceItem>();
                if (workspaceItem != null)
                    this.autoBindingExpression = this.SetBinding(ApplicationViewProperty, new Binding() { Source = workspaceItem, Path = new PropertyPath("ApplicationView") });
            }
            base.OnVisualParentChanged(oldParent);
        }
    }
}